#include <stdint-gcc.h>
#include <stddef.h>
#include "packet.h"
#include "ethmac.h"
#include "uart.h"

/* open a link */
void init_ethmac(void)
{
	/* initialize the packet rx buffer */
	//init_packet();

	/* open ethernet port and wait for connection requests
	   keep trying forever */
	//while (!open_link());

	// Marvell 88E1111 permits preamble suppression
	ethmac_miimoder = 0x132;

	if (open_link()) {
		puts("Link opened");
		putchar('\r');
	} else {
		puts("Failed to open link");
		putchar('\r');
	}
}

/* return 1 if link comes up */
int open_link(void)
{
	int packet;
	int n;
	unsigned int d32;

	/* Disable Ethmac interrupt in interrupt controller */
	//*(unsigned int *) ( ADR_AMBER_IC_IRQ0_ENABLECLR ) = 0x100;

	/* Set my MAC address */
	d32 = self_g.mac[2] << 24 |
	      self_g.mac[3] << 16 |
	      self_g.mac[4] <<  8 |
	      self_g.mac[5]       ;
	//*(unsigned int *) ( ADR_ETHMAC_MAC_ADDR0 ) = d32;
	ethmac_macaddr0 = d32;

	d32 = self_g.mac[0] <<  8 |
	      self_g.mac[1]       ;
	//*(unsigned int *) ( ADR_ETHMAC_MAC_ADDR1 ) = d32;
	ethmac_macaddr1 = d32;

	if (!init_phy()) {
		puts("Failed to bring up PHY");
		putchar('\r');
		return 0;
	}

	puts("PHY link up");
	putchar('\r');

	/* Write the Receive Packet Buffer Descriptor */
	/* Buffer Pointer */
	for (packet=0; packet<ETHMAC_RX_BUFFERS; packet++) {
		//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0x204 + packet*8 )
		ethmac_bufdesc[128+packet*2+1] = (uint32_t)eth_rx_buffer[packet];
		/* Ready Rx buffer
		   [31:16] = length in bytes,
		   [15] = empty
		   [14] = Enable IRQ
		   [13] = wrap bit */
		/* set empty flag again */
		if (packet == ETHMAC_RX_BUFFERS-1) /* last receive buffer ? */ {
			/* Set wrap bit is last buffer */
			//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0x200 + packet*8 )
			ethmac_bufdesc[128+packet*2] = 0x0000e000;
		} else {
			//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0x200 + packet*8 )
			ethmac_bufdesc[128+packet*2] = 0x0000c000;
		}
	}

	/* Enable EthMac interrupts in Ethmac core */
	/* Receive frame and receive error botgh enabled */
	/* When a bad frame is received is still gets written to a buffer
	   so needs to be dealt with */
	//*(unsigned int *) ( ADR_ETHMAC_INT_MASK ) = 0xc;
	ethmac_intmask = 0xc;

	/* Enable Ethmac interrupt in interrupt controller */
	//*(unsigned int *) ( ADR_AMBER_IC_IRQ0_ENABLESET ) = 0x100;

	/* Set transmit packet buffer location */
	//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 4 )
	ethmac_bufdesc[1] = (uint32_t)eth_tx_buffer;

	/* Set the ready bit, bit 15, low */
	//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0 )
	ethmac_bufdesc[0] = 0x7800;

	/* Enable Rx & Tx - MODER Register
	   [15] = Add pads to short frames
	   [13] = CRCEN
	   [10] = Enable full duplex
	   [7]  = loopback
	   [5]  = 1 for promiscuous, 0 rx only frames that match mac address
	   [1]  = txen
	   [0]  = rxen  */
	//*(unsigned int *) ( ADR_ETHMAC_MODER ) = 0xa423;
	ethmac_moder = 0xa423;

	return 1;
}

void close_link(void)
{
	/* Disable EthMac interrupts in Ethmac core */
	//*(unsigned int *) ( ADR_ETHMAC_INT_MASK ) = 0x0;
	ethmac_intmask = 0;

	/* Disable Ethmac interrupt in interrupt controller */
	//*(unsigned int *) ( ADR_AMBER_IC_IRQ0_ENABLECLR ) = 0x100;

	/* Disable Rx & Tx - MODER Register
	   [15] = Add pads to short frames
	   [13] = CRCEN
	   [10] = Enable full duplex
	   [7]  = loopback
	   [5]  = 1 for promiscuous, 0 rx only frames that match mac address
	   [1]  = txen
	   [0]  = rxen  */
	//*(unsigned int *) ( ADR_ETHMAC_MODER ) = 0xa420;
	ethmac_moder = 0xa420;

	/* Put the PHY into reset */
	//phy_rst(0);  /* reset is active low */
}

void ethmac_tx_packet(char* buf, int len)
{
	unsigned int status = 0;

	/* copy the packet into the tx buffer */
	memcpy(eth_tx_buffer, buf, len);

	/* Poll the ready bit.
	   Wait until the ready bit is cleared by the ethmac hardware
	   This holds everything up while the packet is being transmitted, but
	   it keeps things simple. */
#if 0
	while (ethmac_bufdesc[0] & 0x8000);
#endif

	/* Enable packet tx
	   [31:16] = length in bytes,
	   [15] = ready
	   [14] = tx int
	   [13] = wrap bit
	   [12] = pad enable for short packets
	   [11] = crc en
	 */
	//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0 )
	ethmac_bufdesc[0] = len<<16 | 0xf800;
}

static const char hextable[16] = {
	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
	'a', 'b', 'c', 'd', 'e', 'f'
};

/* returns 1 if link comes up */
int init_phy(void)
{
	int bmcr;
	int stat;
	int phy_id;
	//int link_up = 1;
	//time_t* link_timer;

	//link_timer = new_timer();

	/* Bring PHY out of reset */
	//phy_rst(1);  /* reset is active low */

	puts("init_phy()\r");

	/* Discover phy addr by searching addrs in order {0,1,2,..., 31} */
	for(phy_id = 0; phy_id < 32; phy_id++) {
		bmcr = mdio_read(phy_id, MII_BMCR);  /* Basic Mode Control Register */
		mdio_read(phy_id, MII_BMSR);
		stat = mdio_read(phy_id, MII_BMSR);
#if 0
		putchar(hextable[bmcr>>12   ]);
		putchar(hextable[bmcr>> 8&15]);
		putchar(hextable[bmcr>> 4&15]);
		putchar(hextable[bmcr    &15]);
		putchar(' ');
		putchar(hextable[stat>>12   ]);
		putchar(hextable[stat>> 8&15]);
		putchar(hextable[stat>> 4&15]);
		putchar(hextable[stat    &15]);
		putchar('\r');
		putchar('\n');
#endif
#if 1
		if(!((bmcr == 0xFFFF) || ((stat == 0) && (bmcr == 0)))) {
			break;
		}
#endif
	}

	/* Failed to find a PHY on the md bus */
	if (phy_id == 32)
		return 0;

	putchar(hextable[phy_id>> 4   ]);
	putchar(hextable[phy_id    &15]);
	putchar('\r');
	putchar('\n');

	/* Reset PHY */
	bmcr = mdio_read(phy_id, MII_BMCR);
	mdio_write(phy_id, MII_BMCR, bmcr | BMCR_RESET);

	/* Advertise that PHY is NOT B1000-T capable */
	/* Set bits 9.8, 9.9 to 0 */
	bmcr = mdio_read(phy_id, MII_CTRL1000);
	mdio_write(phy_id, MII_CTRL1000, bmcr & 0xfcff );

	/* Restart autoneg */
	bmcr = mdio_read(phy_id, MII_BMCR);
	mdio_write(phy_id, MII_BMCR, bmcr | BMCR_ANRESTART);

	/* Wait for link up */
	/* Print PHY status MII_BMSR = Basic Mode Status Register*/
	/* allow a few seconds for the link to come up before giving up */
	//set_timer(link_timer, 5000);

	while (!(mdio_read(phy_id, MII_BMSR) & BMSR_LSTATUS));
#if 0
	{
		if (timer_expired(link_timer)) {
			link_up = 0;
			break;
		}
	}
#endif

	{
		uint16_t bmsr = mdio_read(phy_id, MII_BMSR);
		putchar(hextable[bmsr>>12   ]);
		putchar(hextable[bmsr>> 8&15]);
		putchar(hextable[bmsr>> 4&15]);
		putchar(hextable[bmsr    &15]);
		putchar('\r');
		putchar('\n');
	}

	//return link_up;
	return 1;
}

int mdio_read(int addr, int reg)
{
	return mdio_ctrl(addr, mdi_read, reg, 0);
}

void mdio_write(int addr, int reg, int data)
{
	mdio_ctrl(addr, mdi_write, reg, data);
}

/*
   addr = PHY address
   reg  = register address within PHY
 */
unsigned short mdio_ctrl(unsigned int addr, unsigned int dir, unsigned int reg, unsigned short data)
{
	unsigned int data_out = 0;
	unsigned int i;
	unsigned long flags;

	mdio_ready();

	//*(volatile unsigned int *)(ADR_ETHMAC_MIIADDRESS)
	ethmac_miiaddr = (reg << 8) | (addr & 0x1f);

	if (dir == mdi_write) {
		//*(volatile unsigned int *)(ADR_ETHMAC_MIITXDATA) = data;
		ethmac_miitxd = data;
		/* Execute Write ! */
		//*(volatile unsigned int *)(ADR_ETHMAC_MIICOMMAND) = 0x4;
		ethmac_miicmd = 0x4;
	} else {
		/* Execute Read ! */
		//*(volatile unsigned int *)(ADR_ETHMAC_MIICOMMAND) = 0x2;
		ethmac_miicmd = 0x2;
		mdio_ready();
		//data_out = *(volatile unsigned int *)(ADR_ETHMAC_MIIRXDATA);
		data_out = ethmac_miirxd;
	}

	return (unsigned short) data_out;
}

/* Wait until its ready */
void mdio_ready()
{
	/* Bit 1 is high when the MD i/f is busy */
	while (ethmac_miistat & 0x2);
}

void ethmac_interrupt(void)
{
	int buffer;
	unsigned int intsrc;
	unsigned int rx_buf_status;

	// debug
	putchar('e');

	/* Mask ethmac interrupts */
	//*(volatile unsigned int *) ( ADR_ETHMAC_INT_MASK   ) = 0;
	ethmac_intmask = 0;

	//intsrc = *(volatile unsigned int *) ( ADR_ETHMAC_INT_SOURCE );
	intsrc = ethmac_intsrc;

	if (intsrc) {
		for (buffer=0; buffer<ETHMAC_RX_BUFFERS; buffer++) {

			//rx_buf_status = *(volatile unsigned int *) ( ADR_ETHMAC_BDBASE + 0x200 + buffer*8 );
			rx_buf_status = ethmac_bufdesc[128+buffer*2];

			if ((rx_buf_status & 0x8000) == 0) {
				parse_rx_packet(eth_rx_buffer[buffer], rx_packet_g);

				/* set empty flag again */
				if (buffer == ETHMAC_RX_BUFFERS-1) /* last receive buffer ? */ {
					//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0x200 + buffer*8 )
					ethmac_bufdesc[128+buffer*2] = 0x0000e000;
				} else {
					//*(unsigned int *) ( ADR_ETHMAC_BDBASE + 0x200 + buffer*8 )
					ethmac_bufdesc[128+buffer*2] = 0x0000c000;
				}
			}
		}
	}

	/* Clear all ethmac interrupts */
	//*(volatile unsigned int *) ( ADR_ETHMAC_INT_SOURCE ) = intsrc;
	ethmac_intsrc = intsrc;

	/* UnMask ethmac interrupts */
	//*(volatile unsigned int *) ( ADR_ETHMAC_INT_MASK   ) = 0xc;
	ethmac_intmask = 0xc;
}
